package cn.edu.zust.se.service.file.utils;


import cn.edu.zust.se.service.file.fastdfs.MultipartFileParam;
import cn.edu.zust.se.service.file.service.FileService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.UUID;

/**
 * @author ：陈文浩
 * @date ：Created in 2020/1/12 14:20
 * @description：分片上传工具类
 */

@Component
@RequiredArgsConstructor
public class ChunkUploadUtil {
    @Autowired
    FastDFSClientUtil fastDFSClientUtil;
    private final FileService fileService;


    public String chunkUploadByMappedByteBuffer(MultipartFileParam param, String filePath) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        if (param.getTaskId() == null || "".equals(param.getTaskId())) {
            param.setTaskId(UUID.randomUUID().toString());
        }
        /**
         *
         * 1：创建临时文件，和源文件一个路径
         * 2：如果文件路径不存在重新创建
         */
        String fileName = param.getFile().getOriginalFilename();
        String tempFileName = param.getTaskId() + fileName.substring(fileName.lastIndexOf(".")) + "_tmp";
        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        File tempFile = new File(filePath, tempFileName);
        //第一步
        RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
        //第二步
        FileChannel fileChannel = raf.getChannel();
        //第三步 计算偏移量
        long position = (param.getChunkNumber() - 1) * param.getChunkSize();
        //第四步
        byte[] fileData = param.getFile().getBytes();
        //第五步
        long end = position + fileData.length - 1;
        fileChannel.position(position);
        fileChannel.write(ByteBuffer.wrap(fileData));
        //使用 fileChannel.map的方式速度更快，但是容易产生IO操作，无建议使用
        fileChannel.force(true);
        fileChannel.close();
        raf.close();
        //第八步
        boolean isComplete = checkUploadStatus(param, fileName, filePath);
        if (isComplete) {
            //重命名文件

            String newName = param.getFile().getOriginalFilename();
            renameFile(tempFile, newName);
//            File file = new File(filePath + "\\" + newName);
//            FileItem fileItem = this.getMultipartFile(file, "test");
//            String fileUrl = fastDFSClientUtil.uploadFile((MultipartFile) file);
//            cn.edu.zust.se.api.entity.File uploadFile = new cn.edu.zust.se.api.entity.File();
//            uploadFile.setPath(fileUrl);
//            uploadFile.setOriginalName(file.getName());
//            uploadFile.setSize(String.valueOf(file.get));
//            uploadFile.setType(SimpleFileUtil.getFileTypeCode(file));
//            uploadFile.setFolderId(folderId);
//            fileService.insertFile(uploadFile);
            return newName;
        }
        return param.getTaskId();
    }

    /**
     * 文件重命名
     *
     * @param toBeRenamed   将要修改名字的文件
     * @param toFileNewName 新的名字
     * @return
     */
    public void renameFile(File toBeRenamed, String toFileNewName) {
        //检查要重命名的文件是否存在，是否是文件
        if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
            return;
        }
        String p = toBeRenamed.getParent();
        File newFile = new File(p + File.separatorChar + toFileNewName);
        //修改文件名
        toBeRenamed.renameTo(newFile);

    }

    /**
     * 检查文件上传进度
     *
     * @return
     */
    public boolean checkUploadStatus(MultipartFileParam param, String fileName, String filePath) throws IOException {
        File confFile = new File(filePath, fileName + ".conf");
        RandomAccessFile confAccessFile = new RandomAccessFile(confFile, "rw");
        //设置文件长度
        confAccessFile.setLength(param.getTotalChunks());
        //设置起始偏移量
        confAccessFile.seek(param.getChunkNumber() - 1);
        //将指定的一个字节写入文件中 127，
        confAccessFile.write(Byte.MAX_VALUE);
        byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);
        confAccessFile.close();//不关闭会造成无法占用
        //这一段逻辑有点复杂，看的时候思考了好久，创建conf文件文件长度为总分片数，每上传一个分块即向conf文件中写入一个127，那么没上传的位置就是默认的0,已上传的就是Byte.MAX_VALUE 127
        for (int i = 0; i < completeStatusList.length; i++) {
            if (completeStatusList[i] != Byte.MAX_VALUE) {
                return false;
            }
        }
        //如果全部文件上传完成，删除conf文件
        confFile.delete();
        return true;
    }

    private FileItem getMultipartFile(File file, String fieldName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        FileItem item = factory.createItem(fieldName, "text/plain", true, file.getName());
        int bytesRead = 0;
        int len = 8192;
        byte[] buffer = new byte[len];
        try {
            FileInputStream fis = new FileInputStream(file);
            OutputStream os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, len)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return item;
    }

}
